#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>

// myshell的环境变量
#define MAX_ENV_SIZE 100
char *my_env[MAX_ENV_SIZE];
int env_count = 0;

// cwd---当前用户工作目录
#define MAX_CWD_SIZE 1024
char cwd[MAX_CWD_SIZE];
char pwdOfcwd[MAX_CWD_SIZE + 6];

// 主机名hostname
#define MAX_HOST_SIZE 256
char hostname[MAX_HOST_SIZE];

// 命令行参数
#define MAX_ARGC 128
char* my_argv[MAX_ARGC];
int my_argc = 0;

// 命令行输入的最大值
#define MAX_COMMAND_LEN 1024

// 最近的一次指令执行的退出码
int last_exit_code = 0;

// 默认字符串
const char* default_str = "None";

// 重定向状态
enum redir_status
{
    NONE_REDIR,
    INPUT_REDIR,
    OUTPUT_REDIR,
    APPEND_REDIR
};
enum redir_status redir = NONE_REDIR;
#define MAX_FILENAME 1024
char filename[MAX_FILENAME];

// 初始化环境变量
void InitEnv()
{
    memset(my_env, 0, sizeof(my_env));
    extern char** environ;
    // 本来应该从配置文件中读取，但为了方便就直接拷贝bash的环境变量
    for(env_count = 0; environ[env_count]; env_count++)
    {
        my_env[env_count] = (char*)malloc(strlen(environ[env_count]) + 1);
        strcpy(my_env[env_count], environ[env_count]);
    }
    my_env[env_count++] = NULL;
    // 让environ指向环境变量表
    environ = my_env;
}

// 打印环境变量，用于调试
void PrintEnv()
{
    extern char** environ;
    for(int i = 0; environ[i]; i++)
    {
        printf("%s\n", environ[i]);
    }
}

// 获取用户名
const char* GetUserName()
{
    const char* username = getenv("USER");
    return username == NULL ? default_str : username;
}

// 获取主机名
const char* GetHostName()
{
    // Ubuntu中没有HOSTNAME环境变量
    // const char* hostname = getenv("HOSTNAME");
    return gethostname(hostname, sizeof(hostname)) != 0 ? default_str : hostname;
}

// 获取当前工作目录
const char* GetPwd()
{
    const char* pwd = getcwd(cwd, sizeof(cwd));
    // 更新pwd
    if(pwd != NULL)
    {
        snprintf(pwdOfcwd, sizeof(pwdOfcwd), "PWD=%s", cwd);
        putenv(pwdOfcwd);
    }
    return pwd == NULL ? default_str : pwd;
}

// 最后一级路径，也叫"基本名称"
const char* GetBaseName()
{
    const char* pwd = GetPwd();
    int pos = strlen(pwd) - 1;
    while(pos > 0 && pwd[pos] != '/')
        pos--;
    if(pwd[pos] == '/') pos++;
    return (pwd + pos);
}

// 获取家目录
const char* GetHome()
{
    const char* home = getenv("HOME");
    return home == NULL ? default_str : home;
}

void PrintCommandPrompt()
{
    // CentOs的格式，感觉更简洁清晰一些
    printf("[%s@%s %s]# ", GetUserName(), GetHostName(), GetBaseName());
}

bool GetCommandLine(char* commandline, int n)
{
    char* str = fgets(commandline, n, stdin);
    if(str == NULL) return false;

    // 清理'\n'，顺便在结尾加上'\0'
    commandline[strlen(commandline) - 1] = '\0';
    if(strlen(commandline) == 0) return false;
    return true;
}

bool ParseCommandLine(char* commandline)
{
    my_argc = 0;
    static const char sep[] = {" "};
    for(my_argv[my_argc] = strtok(commandline, sep); my_argv[my_argc++]; my_argv[my_argc] = strtok(NULL, sep)){;}
    my_argc--;
    return my_argc > 0 ? true : false;
}

// 打印命令行参数，用于调试
void PrintArgv()
{
    for(int i = 0; i < my_argc; i++)
    {
        printf("%s ", my_argv[i]);
    }
    printf("\n");
}

// 内建命令cd
void Cd()
{
    if(my_argc == 1 || strcmp(my_argv[1], "~") == 0 || strcmp(my_argv[1], "-") == 0)
    {
        const char* home = GetHome();
        if(strcmp(home, default_str) != 0)
            chdir(home);
        if(strcmp(my_argv[1], "-") == 0)
            printf("%s\n", home);
    }
    else
    {
        chdir(my_argv[1]);
    }
}

// 内建命令echo
// echo的重定向怎么做？
void Echo()
{
    FILE* file = stdout;
    if(redir == OUTPUT_REDIR)
    {
        file = fopen(filename, "w");
    }
    else if(redir == APPEND_REDIR)
    {
        file = fopen(filename, "a");
    }

    if(strcmp(my_argv[1], "$?") == 0)
    {
        fprintf(file, "%d\n", last_exit_code);
    }
    else if(my_argv[1][0] == '$')
    {
        const char* env = getenv(my_argv[1] + 1);
        if(env)
            fprintf(file, "%s\n", env);
    }
    else
    {
        fprintf(file, "%s\n", my_argv[1]);
    }

    if(file != stdout)
        fclose(file);
}

bool CheckAndExecBuiltin()
{
    if(strcmp(my_argv[0], "cd") == 0)
    {
        Cd();
        return true;
    }
    else if(strcmp(my_argv[0], "echo") == 0)
    {
        Echo();
        return true;
    }
    else if(strcmp(my_argv[0], "export") == 0)
    {
        // Todo
        return true;
    }
    else if(strcmp(my_argv[0], "alias") == 0)
    {
        // Todo
        return true;
    }
    else
    {
        return false;
    }
}

// 执行
void Execute()
{
    pid_t id = fork();

    if(id == 0)
    {
        int fd = 0;
        if(redir == INPUT_REDIR)
        {
            fd = open(filename, O_RDONLY);
            if(fd < 0) exit(1);
            dup2(fd, 0);
        }
        else if(redir == OUTPUT_REDIR)
        {
            fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
            if(fd < 0) exit(1);
            dup2(fd, 1);
        }
        else if(redir == APPEND_REDIR)
        {
            fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
            if(fd < 0) exit(1);
            dup2(fd, 1);
        }

        execvp(my_argv[0], my_argv);
        exit(1);
    }

    redir = NONE_REDIR;
    int status;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
        last_exit_code = WEXITSTATUS(status);
}

void RedirCheck(char* commandline)
{
    redir = NONE_REDIR;
    int len = strlen(commandline);
    int pos = 0;
    for(pos = 0; pos < len; pos++)
    {
        // 输入重定向
        if(commandline[pos] == '<')
        {
            commandline[pos] = '\0';
            redir = INPUT_REDIR;
            break;
        }
        else if(commandline[pos] == '>')
        {
            commandline[pos] = '\0';
            // 追加重定向
            if(pos < len - 1 && commandline[pos + 1] == '>')
            {
                redir = APPEND_REDIR;
                pos++;
            }
            // 输出重定向
            else
            {
                redir = OUTPUT_REDIR;
            }
            break;
        }
    }
    pos++;
    // 发生了重定向，提取文件名
    if(pos != len)
    {
        while(commandline[pos] == ' '){pos++;}
        int i = 0;
        for(i = 0; pos + i < len; i++)
        {
            filename[i] = commandline[pos + i];
        }
        filename[i] = '\0';
    }
}

int main()
{
    // 初始化环境变量
    InitEnv();
    // PrintEnv();
    while(true)
    {
        // 打印命令行提示符
        PrintCommandPrompt();

        // 获取命令行输入
        char commandline[MAX_COMMAND_LEN];
        if(!GetCommandLine(commandline, sizeof(commandline)))
            continue;

        // 判断是否有重定向
        RedirCheck(commandline);

        // 命令行解析
        if(!ParseCommandLine(commandline))
            continue;
        // PrintArgv();

        // 判断是否有为内建命令，如果是就执行
        if(CheckAndExecBuiltin())
            continue;

        // 执行命令
        Execute();
    }
    return 0;
}
